<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Base\Order;
use App\Models\GameLog\LogPayRecord;
use App\Models\Game\PlayerGamedata;
use App\Models\Login\Account;
use App\Service\AccountService;
use App\Service\ChannelService;
use App\Service\PlayerService;
use App\Service\RequestService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

/**
 * 支付.
 */
class LeyouPayController extends Controller {
    const SUCCESS = 'success';
    const FAIL = 'error';
    const QUICK_SUCCESS = 'SUCCESS';
    const QUICK_FAIL = 'FAIL';
    const SIGN_ERROR = 'errorSign';

    /**
     * 安卓支付发货
     * @return string
     */
    public function shipmentsAndroid()
    {
        $publicKey = '0387c45606090bae7e945cea835ddbbb';
        return $this->deliverGood($publicKey);
    }

    /**
     * ios支付发货
     * @return string
     */
    public function shipmentsIos()
    {
        $publicKey = 'f4a49121706dbe04efef0f99347c4cc7';
        return $this->deliverGood($publicKey);
    }


    /**
     * 支付回调逻辑.
     *
     * @param Request $request
     *
     * @return string
     */
    public function deliverGood($publicKey) {
        $post = request()->all();
        if (empty($post)) {
            return self::FAIL;
        }
        $server_id = $post['serverid'];
        Log::warning('收到支付回调:', $post);

        //验证签名
        if (false == $this->verfiy($post, $publicKey)) {
            Log::error('签名验证失败!');
            return self::SIGN_ERROR;
        }

        $attach = explode('*',$post['attach']);

        $order_sn = $attach[0]; //游戏订单号
        //判断是否充值过
        $one = $this->is_pay($order_sn);
        if ($one === false) {
            return self::SUCCESS;
        }
        //查询游戏订单号
        $order = $order = $this->gameOrder($server_id,$order_sn);
        if ($order == false) {
            return self::FAIL;
        }

        $playerData = PlayerService::getPayerInfo($server_id, $order->playerid);

        $ret = $this->exePay($order->count, $post['amount'], $one, $order, $playerData,
            $server_id, $order_sn, $post['orderid']);
        if ($ret) {
            return self::SUCCESS;
        } else {
            Log::error('更新状态失败!');
            return self::FAIL;
        }
    }

    /**
     * 获取玩家渠道id
     * @param $roleId
     * @param $serverId
     * @return int
     */
    private function getChannelId($roleId,$serverId)
    {
        $roleInfo = PlayerService::getPayerInfo($serverId,$roleId);
        $userId = $roleInfo->userId;
        return AccountService::getPlayerChannel($userId);
    }

    /**
     * ios支付回调发货
     * @param Request $request [nt_data,sign,md5Sign]
     * @return string
     * @doc https://www.quicksdk.com/doc-0.html?cid=27&tdsourcetag=s_pctim_aiomsg
     */
    public function quickDeliverGood(Request $request) {
        $params = $params = $request->all();
        if ($params == null) {
            return self::QUICK_FAIL;
        }
        Log::warning('收到支付回调:quick_ios', $params);
        $callbackKey = "86149707796490256996845142839667";
        $md5Key = "dlbcwdwh9di7jvhkudwl2usvkmsgoxtc";
        //验证签名
        if ($this->verifyQuickSign($params, $md5Key) == false) {
            return self::QUICK_FAIL;
        }
        $nt_data_decode = $this->decode($params['nt_data'], $callbackKey);
        Log::warning('解密nt_data:', [$nt_data_decode]);
        $nt_data = xmlToArray($nt_data_decode);
        $nt_data = $nt_data['message'];
        //游戏方订单号
        $order_sn = $nt_data['game_order'];
        $extras_params = $nt_data['extras_params'];
        $extras = explode('*', $extras_params);
        $server_id = $extras[1];

        if ($nt_data['status'] == 1) {
            Log::error('参数状态未支付!');
            return self::QUICK_FAIL;
        }

        //判断是否充值过
        $one = $this->is_pay($order_sn);
        if ($one === false) {
            return self::QUICK_SUCCESS;
        }
        //查询游戏订单号
        $order = $this->gameOrder($server_id,$order_sn);
        if ($order == false) {
            return self::QUICK_FAIL;
        }
        $playerData = PlayerService::getPayerInfo($server_id, $order->playerid);
//		$publicKey = ChannelService::getChannelKey($playerData->channelId);

        $ret = $this->exePay($order->count, $nt_data['amount'], $one, $order, $playerData,
            $server_id, $order_sn, $nt_data['order_no']);
        if ($ret) {
            return self::QUICK_SUCCESS;
        } else {
            Log::error('更新状态失败!');
            return self::QUICK_FAIL;
        }
    }

    /** 判断是否充值过
     * @param $order_sn
     * @return bool|Order
     */
    private function is_pay($order_sn) {
        $one = Order::where('order_sn', $order_sn)->first();
        if ($one  && 1 == $one->status) {
            Log::warning('订单已存在并支付');
            return false;
        } else {
            return $one;
        }
    }

    /**
     * 执行支付发货
     * @param $count
     * @param $amount
     * @param Order $one
     * @param LogPayRecord $order
     * @param $playerData
     * @param $server_id
     * @param $order_sn
     * @param $plat_order
     * @return boolean
     */
    private function exePay($count, $amount, $one, LogPayRecord $order, $playerData, $server_id, $order_sn, $plat_order) {

        $channlId = AccountService::getPlayerChannel($playerData->userId);
        //判断金额
        if ($count != $amount) {
            Log::error('金额不一致!');
            return false;
        }
        //写入本地订单表
        if (null == $one) {
            $local = $this->addOrder($order, $playerData, $channlId, $server_id, $order_sn, $plat_order);
            if (!$local) {
                Log::error('写入本地失败!');
                return false;
            }
        }
        //通知游戏服
        $send = RequestService::sendOrderPay($server_id, $order_sn, $order->platformId,
            $order->playerid, $order->accountId);
        if (!$send) {
            Log::error('发送给游戏服失败!');
            return false;
        }
        //更新订单状态
        if ($one) {
            $one->status = 1;
            $ret = $one->save();
        } else {
            $local->status = 1;
            $ret = $local->save();
        }
        return $ret;
    }

    /**
     * 验证支付签名.
     *
     * @param $post
     * @param string $app_key
     *
     * @return bool
     */
    protected function verfiy($post, $app_key) {
        $sign = $post['sign'];

        $md5str = "orderid=".urlencode($post['orderid'])."&username=".urlencode($post['username'])."&gameid=".urlencode($post['gameid'])."&roleid="
            .urlencode($post['roleid'])."&serverid=".urlencode($post['serverid'])."&paytype=".urlencode($post['paytype'])."&amount=".urlencode($post['amount']).
            "&paytime=".urlencode($post['paytime'])."&attach=".urlencode($post['attach'])."&appkey=".urlencode($app_key);

        $verify_sign = md5($md5str);

        //验证签名
        if (0 !== strcasecmp($sign, $verify_sign)) {
            return false;
        }
        return true;
    }

    /**
     * 写入订单记录.
     *
     * @param LogPayRecord   $order
     * @param PlayerGamedata $player
     * @param $channlId
     * @param $server_id
     * @param $order_sn
     * @param $tcd
     *
     * @return Order
     */
    protected function addOrder(LogPayRecord $order, $player, $channlId, $server_id, $order_sn, $tcd) {
        $localOrder = new Order();
        $localOrder->order_sn = "{$order_sn}";
        $localOrder->channel_id = $channlId;
        $localOrder->userid = "{$order->userid}";
        $localOrder->player_id = "{$order->playerid}";
        $localOrder->amount = $order->count;
        $localOrder->status = 0;
        $localOrder->order_time = $order->time;
        $localOrder->player_name = $player->name;
        $localOrder->userid = $player->userId;
        $localOrder->platform_id = $order->platformId;
        $localOrder->server_id = $server_id;
        $localOrder->platform_order_sn = $tcd;
        $localOrder->pay_time = time();
        $localOrder->charge_level = $order->accountId;
        $localOrder->save();

        return $localOrder;
    }

    /**
     * 查询游戏服订单记录.
     *
     * @param $server_id
     * @param $order_sn
     *
     * @return bool
     */
    protected function gameOrder($server_id, $order_sn) {
        if (!setDB($server_id)) {
            return false;
        }
        $record = LogPayRecord::where('orderId', $order_sn)->first();
        if (!$record) {
            Log::error('订单不存在:', ['server_id' => $server_id, 'order_sn' => $order_sn]);
            return false;
        } else {
            return $record;
        }
    }

    /**
     * quickIos签名验证
     * @param $params
     * @param $md5Key
     * @return bool
     */
    private function verifyQuickSign($params, $md5Key) {

        $localMd5Sign = md5($params['nt_data'] . $params['sign'] . $md5Key);
        if (0 !== strcasecmp($params['md5Sign'], $localMd5Sign)) {
            return false;
        }
        return true;
    }

    /**
     * 解密方法
     * $strEncode 密文
     * $keys 解密密钥 为游戏接入时分配的 callback_key
     */
    private function decode($strEncode, $keys) {
        if (empty($strEncode)) {
            return $strEncode;
        }
        preg_match_all('(\d+)', $strEncode, $list);
        $list = $list[0];
        if (count($list) > 0) {
            $keys = self::getBytes($keys);
            for ($i = 0; $i < count($list); $i++) {
                $keyVar = $keys[$i % count($keys)];
                $data[$i] = $list[$i] - (0xff & $keyVar);
            }
            return self::toStr($data);
        } else {
            return $strEncode;
        }
    }

    /**
     * MD5签名替换
     */
    static private function replaceMD5($md5) {

        strtolower($md5);
        $bytes = self::getBytes($md5);

        $len = count($bytes);

        if ($len >= 23) {
            $change = $bytes[1];
            $bytes[1] = $bytes[13];
            $bytes[13] = $change;

            $change2 = $bytes[5];
            $bytes[5] = $bytes[17];
            $bytes[17] = $change2;

            $change3 = $bytes[7];
            $bytes[7] = $bytes[23];
            $bytes[23] = $change3;
        } else {
            return $md5;
        }

        return self::toStr($bytes);
    }

    /**
     * 转成字符数据
     */
    private static function getBytes($string) {
        $bytes = array();
        for ($i = 0; $i < strlen($string); $i++) {
            $bytes[] = ord($string[$i]);
        }
        return $bytes;
    }

    /**
     * 转化字符串
     */
    private static function toStr($bytes) {
        $str = '';
        foreach ($bytes as $ch) {
            $str .= chr($ch);
        }
        return $str;
    }

}
